// ============================== 导入依赖 ============================== //

import CORE from '@/CORE'
import mixPath from '../mixPath'

const getWords = (label, min) => {
  const text = label.replace(/[\u4e00-\u9fa5]/g, '**')
  if (text.length < min * 2) return min
  return (text.length / 2) | 0
}

// ============================== 导出混合 ============================== //

export default {
  /**
   * 混入列表 (配置选项合并)
   */
  mixins: [mixPath],

  /**
   * 属性注册 (抽取以便查阅)
   */
  props: CORE.WEB.FormCommon,

  /**
   * 状态数据 (函数返回对象)
   */
  data() {
    return {
      model: undefined, // 内部绑定值
      EMPTY: undefined, // 空值常量
      updateTime: 0, // 更新时间戳
    }
  },

  /**
   * 计算属性 (慎用箭头函数)
   */
  computed: {
    /**
     * 父级的 Web 组件
     */
    parentWeb() {
      return CORE.componentParent(this, 'WebForm', 'WebShow') || { $props: {} }
    },

    /**
     * 模块路径
     */
    path() {
      const { parentWeb } = this
      return parentWeb.$props.path || ''
    },

    /**
     * 所属对象
     */
    obj() {
      const { one, state } = this
      if (one) return one

      const { parentWeb } = this
      return parentWeb.$props.one || state.one || {}
    },

    /**
     * 是否已禁用
     */
    isDisabled() {
      const { parentWeb, state, disabled, noDisable, onlyAdd } = this
      if (noDisable) return false
      return parentWeb.$props.disabled || disabled || state.modal === 'show' || (onlyAdd && state.modal !== 'add')
    },

    /**
     * 占位提示
     */
    placeholder() {
      const { isDisabled, noTip, tip, tipPre, label } = this
      if (isDisabled || noTip) return ''
      return tip === null ? `${tipPre}${label}` : tip
    },

    /**
     * 绑定列表
     */
    bindList() {
      const { bind } = this
      if (CORE.isArray(bind)) return bind.map((item) => item.trim())
      if (!CORE.isString(bind)) return []
      if (!bind.includes(',')) return [bind.trim()]
      return bind.split(',').map((item) => item.trim())
    },

    /**
     * 数据列表
     */
    valueList() {
      return this.bindList.map((bind) => this.obj[bind])
    },

    /**
     * 是否为多个绑定
     */
    isMulti() {
      return this.bindList.length > 1
    },

    /**
     * 属性名称
     */
    prop() {
      return this.bindList[0] || ''
    },

    /**
     * 尺寸
     */
    size() {
      const { initSize, parentWeb } = this
      if (this.normal) return null
      if (this.medium) return 'medium'
      if (this.small) return 'small'
      if (this.mini) return 'mini'
      if (initSize) return initSize || null
      if (parentWeb && parentWeb.size) return parentWeb.size
      return null
    },

    /**
     * 公共属性
     */
    commonProps() {
      const { elProps, className, prop, isDisabled, noClear, placeholder, size } = this
      const disableStr = isDisabled ? ` ${className}-disabled` : ''
      const alginStr = this.center ? ' alignCenter' : this.right ? ' alignRight' : ''
      return {
        ...elProps,
        name: prop,
        class: `${className}${disableStr}${alginStr}`,
        disabled: isDisabled,
        clearable: !noClear,
        placeholder,
        size,
      }
    },

    /**
     * 是否有标签插槽
     */
    hasLabelSlot() {
      return this.formItem.label || (this.$scopedSlots && this.$scopedSlots.label)
    },

    /**
     * 校验方法
     */
    validator() {
      const { required, check, min, minErr, obj, bind } = this
      return (rule, value, callback) => {
        let errMsg
        if (min && CORE.isString(value) && value.length < min) errMsg = minErr.replace('{min}', min)
        if (check && !(required && CORE.isEmpty(value))) errMsg = check(value, obj, bind) // 必填且为空，不走此处校验
        errMsg ? callback(new Error(errMsg)) : callback()
      }
    },

    /**
     * 规则列表
     */
    rules() {
      const { isDisabled, required, reg, regErr, placeholder, validator, tipPre } = this
      if (isDisabled) return null

      const rules = []
      if (required) rules.push({ required: true, message: placeholder || tipPre })
      if (reg) rules.push({ pattern: reg, message: regErr })
      if (validator) rules.push({ validator })
      return rules.length ? rules : null
    },

    /**
     * 表项样式类
     */
    itemClass() {
      if (this.flex0) return 'flex0'

      // flex30 到 flex1
      const array30 = Array(30).fill()
      for (const i of array30.map((_, i) => 30 - i)) {
        if (this[`flex${i}`]) return `flex${i}`
      }

      if (this.width) return 'width'
      if (this.half) return 'half'
      if (this.third) return 'third'
      if (this.third2) return 'third2'
      if (this.quar) return 'quar'
      if (this.quar3) return 'quar3'
      if (this.fifth) return 'fifth'
      if (this.fifth2) return 'fifth2'
      if (this.fifth3) return 'fifth3'
      if (this.fifth4) return 'fifth4'
      if (this.sixth) return 'sixth'
      if (this.sixth5) return 'sixth5'
      if (this.eighth) return 'eighth'
      if (this.eighth3) return 'eighth3'
      if (this.eighth5) return 'eighth5'
      if (this.eighth7) return 'eighth7'
      if (this.auto) return 'auto'
      return 'whole'
    },

    /**
     * 表项样式对象
     */
    itemStyle() {
      const { width, mixWidth, maxWidth, getSize } = this
      const style = {}
      if (width) style.width = getSize(width)
      if (mixWidth) style.mixWidth = getSize(mixWidth)
      if (maxWidth) style.maxWidth = getSize(maxWidth)
      return CORE.isEmpty(style) ? null : style
    },

    /**
     * 标签文字个数
     */
    wordNum() {
      const from2to10 = Array(9).fill().map((_, i) => i + 2)
      for (const num of from2to10) {
        if (this[`word${num}`]) return num
      }
      const { label, wordFit, minFit, words } = this
      if (words) return words
      if (wordFit) return getWords(label, minFit)
      return 0
    },

    /**
     * 父组件的标签文字个数
     */
    parentWordNum() {
      const { label, parentWeb } = this
      if (parentWeb.wordNum) return parentWeb.wordNum
      if (parentWeb.wordFit) return getWords(label, parentWeb.minFit)
      return 0
    },

    /**
     * 最终的标签宽度
     */
    myLabelWidth() {
      const { noLabel, getSize, labelWidth, wordNum, parentWordNum, parentWeb } = this
      const getWidth = (el, num) => getSize(`${el.wordSize * num + el.blankWidth}${el.widthUnit}`)

      if (noLabel) return '0' // 无标签
      if (wordNum) return getWidth(this, wordNum) // 当前配置的字数
      if (labelWidth) return getSize(labelWidth) // 当前配置的宽度
      if (parentWordNum) return getWidth(parentWeb, parentWordNum) // 父组件配置的固定字数或计算出的自适应字数
      return null
    },

    /**
     * 表单项配置
     */
    formItem() {
      const { prop, rules, itemClass } = this
      const marginStr = this.noMargin ? ' noMargin' : ''
      return {
        ref: 'formItem',
        key: this.$options.name,
        id: rules && rules.length ? `form-item-${prop}` : null,
        prop,
        label: this.noLabel ? '' : this.label,
        labelWidth: this.myLabelWidth,
        class: `CoreWeb-FormItem ${itemClass}${marginStr}`,
        style: this.itemStyle,
        required: !this.commonProps.disabled && this.required,
        rules,
        size: this.size,
      }
    },
  },

  /**
   * 监听属性 (深度 deep 首次 immediate)
   */
  watch: {
    /**
     * 监听数据列表
     */
    valueList() {
      if (this.isDiff()) {
        this.setModel()
        this.emitChange()
      }
    },

    /**
     * 监听字典列表
     */
    dict() {
      if (this.isDiff()) {
        this.setModel()
        this.emitChange()
      }
    },
  },

  /**
   * 创建完成 (先抽取再调用)
   */
  created() {
    // 键名不存在，则添加空值，以使属性可响应
    const { obj, bindList, EMPTY, $set } = this
    const keys = Object.keys(obj)
    bindList.forEach((item) => !keys.includes(item) && item !== 'slot' && $set(obj, item, EMPTY))

    if (this.isDiff()) {
      this.setModel()
      this.emitChange()
    }
  },

  /**
   * 本地方法 (轻量重要在前)
   */
  methods: {
    /**
     * 是否逻辑不等 (如不等则调用 setModel() ) (可覆盖)
     */
    isDiff() {
      const { obj, prop, bindList, isMulti, dict, model, init, initFirst, EMPTY } = this
      const value = obj[prop]

      // 无值
      if (!value) {
        if (model || init || initFirst) return true
        return value !== EMPTY
      }

      // 有值
      if (isMulti) {
        const values = (dict || []).find(([id]) => value === id) // 匹配的字典
        if (!values || value !== model) return true // 不相等
        return bindList.some((key, i) => obj[key.trim()] !== values[i] || (i ? '' : EMPTY)) // 所有属性都需要相等
      } else {
        return value !== model
      }
    },

    /**
     * 设置内部绑定值 (可覆盖)
     */
    setModel() {
      const { obj, prop, init, EMPTY } = this
      this.model = obj[prop] || init || EMPTY
    },

    /**
     * 获取最终回传值 (可覆盖)
     */
    getValue() {
      const { model } = this
      return model
    },

    /**
     * 是否物理相等 (如不等可直接用 value 赋值给 obj[bind] ) (可覆盖)
     */
    equals(value) {
      const { obj, prop, bindList, isMulti, dict, EMPTY } = this

      if (isMulti) {
        const values = (dict || []).find((item) => item[0] === value) || []
        return bindList.every((key, i) => obj[key.trim()] === values[i] || (i ? '' : EMPTY))
      } else {
        return obj[prop] === value
      }
    },

    /**
     * 提交改变 (可覆盖)
     */
    emitChange() {
      const { obj, prop, bindList, isMulti, dict, EMPTY } = this

      // 获取新值
      const value = this.getValue()

      // 判断改变
      if (this.onlyShow || this.equals(value)) return

      // 更新绑定
      if (isMulti) {
        const values = dict.find((item) => item[0] === value) || []
        bindList.forEach((key, i) => this.$set(obj, key, values[i] || (i ? '' : EMPTY)))
        this.$emit('change', values)
      } else {
        this.$set(obj, prop, value)
        this.$emit('change', value)
      }

      // 提交之后
      this.afterEmit()
    },

    /**
     * 提交之后 (可覆盖)
     */
    afterEmit() {
      setTimeout(() => {
        const { rules, $refs } = this
        if (rules && $refs && $refs.formItem) $refs.formItem.validate()
      }, 0)
    },
  },
}
